//
// DepConfReader
//
// Copyright (C) jextra.net.
//
//  This file is part of the net.jextra.crackdep Library.
//
//  The net.jextra.crackdep Library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  The net.jextra.crackdep Library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with the net.jextra.crackdep Library; if not, write to the Free
//  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
//  02111-1307 USA.
//

package net.jextra.crackdep;

import java.lang.reflect.*;
import java.util.*;
import net.jextra.crack.core.*;
import net.jextra.dep.repo.*;
import org.xml.sax.*;

/**
 * <p>
 * Reads a dependency configuration XML file (conf/dep-config.xml).
 * </p>
 */
public class DepConfReader extends XMLSAXReader
{
    // ============================================================
    // Enums
    // ============================================================

    public enum State
    {
        depConfig( "dep-conf" ),
        style( "style" ),
        styleDisabled( "style" ),
        localRepo( "local-repo" ),
        teamRepo( "team-repo" ),
        teamRepoInfo( "*team-repo-info*" );

        String tag;

        State( String tag )
        {
            this.tag = tag;
        }

        public String tag()
        {
            return tag;
        }

        public static State tagValue( String tag )
        {
            for ( State state : State.values() )
            {
                if ( state.tag().equals( tag ) )
                {
                    return state;
                }
            }

            return null;
        }
    }

    // ============================================================
    // Fields
    // ============================================================

    public static final String FILENAME = "dep-conf.xml";

    private String style;
    private DepRepo localRepo;
    private LinkedHashMap<String, DepRepo> teamRepos;

    private Stack<DepRepoParam> nodeStack;
    private DepRepoParam repoParams;

    // ============================================================
    // Constructors
    // ============================================================

    public DepConfReader( String style )
    {
        this.style = style;

        localRepo = null;
        teamRepos = new LinkedHashMap<String, DepRepo>();
        repoParams = null;
        nodeStack = new Stack<DepRepoParam>();
    }

    // ============================================================
    // Methods
    // ============================================================

    // ----------
    // public
    // ----------

    public DepRepoParam getRepoParams()
    {
        return repoParams;
    }

    public DepRepo getLocalRepo()
    {
        return localRepo;
    }

    public DepRepo getTeamRepo( String teamRepoName )
    {
        return teamRepos.get( teamRepoName );
    }

    public Collection<DepRepo> getTeamRepos()
    {
        return teamRepos.values();
    }

    // ----------
    // protected
    // ----------

    @Override
    protected void startDocument()
        throws Exception
    {
    }

    @Override
    public void endDocument()
        throws Exception
    {
    }

    @Override
    public String startElement( String stateString, String uri, String tagName, String qName, Attributes atts )
        throws Exception
    {
        State state = getState( stateString );
        State newState = null;

        if ( tagName.equalsIgnoreCase( State.depConfig.tag() ) )
        {
            newState = State.depConfig;
        }
        else if ( state == State.depConfig && tagName.equalsIgnoreCase( State.style.tag() ) )
        {
            newState = State.styleDisabled;

            String name = atts.getValue( "name" );
            if ( style != null && style.equalsIgnoreCase( name ) )
            {
                newState = State.style;
            }
        }
        else if ( state == State.style
            && ( tagName.equalsIgnoreCase( State.localRepo.tag() ) || tagName.equalsIgnoreCase( State.teamRepo.tag() ) ) )
        {
            newState = State.tagValue( tagName );

            repoParams = new DepRepoParam( tagName );
            for ( int i = 0; i < atts.getLength(); i++ )
            {
                repoParams.addAttribute( atts.getQName( i ), atts.getValue( i ) );
            }

            pushNode( repoParams );
        }
        else if ( state == State.localRepo || state == State.teamRepo )
        {
            newState = State.teamRepoInfo;

            DepRepoParam node = new DepRepoParam( tagName );
            for ( int i = 0; i < atts.getLength(); i++ )
            {
                node.addAttribute( atts.getQName( i ), atts.getValue( i ) );
            }

            pushNode( node );
        }

        return newState == null ? null : newState.name();
    }

    @Override
    public void characters( String stateString, char[] value, int offset, int count )
        throws Exception
    {
    }

    @Override
    public void endElement( String stateString, String uri, String tagName, String qName )
        throws Exception
    {
        State state = getState( stateString );

        if ( state == State.localRepo )
        {
            localRepo = createRepo( repoParams );
        }
        else if ( state == State.teamRepo )
        {
            DepRepo repo = createRepo( repoParams );
            if ( repo != null )
            {
                teamRepos.put( repo.getName(), repo );
            }
        }
        else if ( state == State.teamRepoInfo )
        {
            popNode();
        }
    }

    // ----------
    // private
    // ----------

    private State getState( String stateString )
    {
        return stateString == null ? null : State.valueOf( stateString );
    }

    private DepRepoParam popNode()
    {
        return nodeStack.pop();
    }

    private void pushNode( DepRepoParam node )
    {
        DepRepoParam currentNode = nodeStack.isEmpty() ? null : nodeStack.peek();
        if ( currentNode != null )
        {
            currentNode.addChild( node );
        }
        nodeStack.push( node );
    }

    private DepRepo createRepo( DepRepoParam params )
        throws Exception
    {
        @SuppressWarnings( "unchecked" )
        Class<DepRepo> repoClass = (Class<DepRepo>) Class.forName( params.getAttribute( "class" ) );
        Constructor<DepRepo> cons = repoClass.getDeclaredConstructor();
        cons.setAccessible( true );
        DepRepo repo = cons.newInstance();

        try
        {
            repo.init( params );

            return repo;
        }
        catch ( Exception ex )
        {
            System.err.println( "WARNING: Unable to connect to repo " + repo.getName() );
        }

        return null;
    }
}
